package org.gpf;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.SequenceInputStream;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;

import org.apache.commons.codec.binary.Hex;

public class FileUtil {

	/**
	 * 将构造方法私有化，防止其产生实例化对象
	 */
	private FileUtil() {

	}

	public static boolean copyFile(String src, String des) throws IOException {

		boolean flag = false;

		File srcFile = new File(src);
		File desFile = new File(des);

		if (!srcFile.exists()) {
			flag = false;
			throw new IllegalArgumentException("源文件不存在！");
		}

		FileInputStream fis = new FileInputStream(srcFile);
		FileOutputStream fos = new FileOutputStream(desFile);

		byte[] temp = new byte[(int) srcFile.length()];
		fis.read(temp); // 将源文件读入到字节数组
		fos.write(temp); // 将字节数组中的内容写入到输出流

		flag = true;
		fis.close(); // 关闭流
		fos.close();
		return flag;

	}

	/**
	 * 使用java新IO中的通道复制文件
	 * 
	 * @param src
	 * @param des
	 * @return
	 * @throws IOException
	 */
	public static boolean copyFileByChannel(String src, String des)
			throws IOException {

		boolean flag = false;
		File srcFile = new File(src);
		File desFile = new File(des);

		if (!srcFile.exists()) {
			flag = false;
			throw new IllegalArgumentException("源文件不存在！");
		}

		FileInputStream fis = new FileInputStream(srcFile);
		FileOutputStream fos = new FileOutputStream(desFile);
		FileChannel fin = fis.getChannel();
		FileChannel fout = fos.getChannel();
		fin.transferTo(0, fin.size(), fout);

		flag = true;
		fos.close();
		fis.close();

		return flag;
	}

	public static boolean deleteFile(String filename) {

		File file = new File(filename);
		return file.delete();
	}

	public static boolean moveFile(String src, String des) throws IOException {

		if (!copyFile(src, des)) {
			return false;
		}
		if (!deleteFile(src)) {
			return false;
		}

		return true;

	}

	/**
	 * 读取文本文件 如果用户指定生成一个新的文件则以时间戳生成一个新的文件保存文本内容
	 * 
	 * @param is2newFile
	 * @return
	 * @throws IOException
	 */
	public static boolean readTextFile(String src, boolean is2newFile)
			throws IOException {

		boolean flag = false;
		File srcFile = new File(src);
		if (!srcFile.isFile()) {
			flag = false;
			throw new IllegalArgumentException(src + "不是文件！");
		}
		if (is2newFile) {
			String filename = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + "_tmp.txt";
			File file = new File(filename);
			String des = file.getAbsolutePath();
			if (copyFile(src, des)) {
				System.out.println("文件已保存至：" + file.getAbsolutePath() + "！程序将使用记事本为您打开文件。");
				Runtime.getRuntime().exec("notepad " + filename);
				flag = true;
			} else {
				flag = false;
			}

		} else {
			Scanner scanner = new Scanner(new FileInputStream(src));
			scanner.useDelimiter("\r\n");
			while (scanner.hasNext()) {
				System.out.println(scanner.next());
			}

			scanner.close();
			flag = true;
		}
		return flag;
	}

	/**
	 * 以Linux的ls命令的方式显示文件内容
	 * 
	 * @param filename
	 */
	public static void printFileInfo(String filename) {

		File file = new File(filename);

		if (file.isDirectory()) {
			System.out.print("d");
		} else if (file.isFile()) {
			System.out.print("-");
		} else if (file.canRead()) {
			System.out.print("r");
		} else if (file.canWrite()) {
			System.out.print("w");
		} else if (file.canExecute()) {
			System.out.print("x");
		} else if (file.isHidden()) {
			System.out.print("h");
		}
		System.out.print("\t" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(file.lastModified())));
		System.out.print("\t" + file.length() + "B");
		System.out.println("\t" + file.getAbsolutePath());
	}

	/**
	 * 打印文件的16进制 一个字节是8位，需要使用2个16进制数字表示，如果一个数小于等于F，那么它只有1位，需要在前面补0
	 */
	public static void printHex(String filename) throws IOException {

		FileInputStream fis = new FileInputStream(filename);
		int temp = 0;
		int count = 0;
		while ((temp = fis.read()) != -1) {
			if (temp <= 0xF) {
				System.out.print("0");
			}
			System.out.print(Integer.toHexString(temp) + "  ");
			if (++count % 8 == 0) {
				System.out.println();
			}
		}
		fis.close();
	}

	/**
	 * 根据文件的深度，前面补上适当的缩进
	 * @param depth 文件深度
	 * @return
	 */
	private static String getDepth(int depth){
	
		StringBuilder sb = new StringBuilder();
		sb.append("|--");
		for (int i = 0; i < depth; i++) {
//			sb.append("|--");
			sb.insert(0, "|  ");
		}
		return sb.toString();
	}
	
	/**
	 * 列出当前目录以及子目录中的所有文件
	 * @param dirname 目录名
	 * @param depth 文件深度（调用的时候传入0即可）
	 * @throws IOException
	 */
	public static void listDirectory(String dirname,int depth) throws IOException {

		File file = new File(dirname);
		if (!file.exists()) {
			throw new IllegalArgumentException("目录" + file + "不存在！");
		}
		if (!file.isDirectory()) {
			throw new IllegalArgumentException(file + "不是目录！");
		}

		depth++;
		File[] files = file.listFiles();
		if (files != null && files.length > 0) {
			for (File f : files) {
				if (f.isDirectory()) {
					listDirectory(f.getAbsolutePath(),depth);
				} else {
					System.out.println(getDepth(depth) + f);
				}
			}
		}
	}

	/**
	 * 得到文件的MD5
	 */
	public static String getMD5(String filename) throws IOException,
			NoSuchAlgorithmException {

		File file = new File(filename);
		if (file.isFile()) {
			FileInputStream fis = new FileInputStream(file);
			byte[] buf = new byte[(int) file.length()];
			fis.read(buf);
			MessageDigest md = MessageDigest.getInstance("MD5");
			byte[] temp = md.digest(buf);

			fis.close();
			return Hex.encodeHexString(temp);
		} else {
			return null;
		}
	}

	/**
	 * 得到文件的hash
	 */
	public static String getHash(String filename) throws IOException,
			NoSuchAlgorithmException {

		File file = new File(filename);
		if (file.isFile()) {
			FileInputStream fis = new FileInputStream(file);
			ByteArrayOutputStream baos = new ByteArrayOutputStream();

			int temp = 0;
			while ((temp = fis.read()) != -1) {
				baos.write(temp);
			}

			fis.close();
			MessageDigest messageDigest = MessageDigest.getInstance("SHA");
			return Hex.encodeHexString(messageDigest.digest(baos.toByteArray()));
		} else {
			return null;
		}

	}

	/**
	 * 过滤当前目录下的特定后缀名的文件，并将文件名保存到字符串中 不同文件名之间以逗号分隔
	 * @fileName :文件名或者目录名
	 * @filter :过滤器，特定的后缀，*或者空字符串表示匹配所有
	 */
	static StringBuilder result = new StringBuilder();
	static int count = 0;

	public static String getTpyeFile(String fileName, String filter) {

		File file = new File(fileName);
		if (filter == null || filter.equals("*")) {
			filter = "";
		}
		if (file.isFile() && file.getName().endsWith(filter)) {
			result.append(file.getAbsolutePath() + ",");
			count++;
		}
		if (file.isDirectory()) {
			File[] files = file.listFiles();
			for (File f : files) {
				getTpyeFile(f.getAbsolutePath(), filter);
			}
		}
		return result.toString();
	}
	
	/**
	 * 将指定目录下的java圆文件保存在list集合中
	 * @param dir 目录
	 * @param fileList 存储文件的集合(在调用方法的时候传入)
	 */
	public static void storeFile2List(File dir,List<File> fileList){
		
		File[] files = dir.listFiles();
		
		for (File file : files) {
			if(file.isDirectory()){
				storeFile2List(file, fileList);
			} else {
				if(file.getName().endsWith(".java")){
					fileList.add(file);
				}
			}	
		}
	}
	
	/**
	 * 将文件列表中的文件保存到特定的java列表文件中
	 * @param fileliList装有文件对象的容器
	 * @param javaListFile java列表文件
	 * @throws FileNotFoundException 
	 */
	public static void write2File(List<File> fileliList,String javaListFile) throws FileNotFoundException{
		
		PrintWriter pw = new PrintWriter(javaListFile);
		for (File file : fileliList) {
			pw.println(file.getAbsolutePath());
		}
		pw.close();
	}
	
	/**
	 * 转换文件编码
	 * @param file:需要转换的文件名
	 * @param fromCharset：文件的原始编码
	 * @param toCharset：需要转换的编码
	 * @return
	 */
	public static boolean convertEncoding(File file, String fromCharset,
			String toCharset) {

		try {
			InputStreamReader isr = new InputStreamReader(new FileInputStream(
					file), fromCharset);
			File tempFile = new File("tmp");
			OutputStreamWriter oos = new OutputStreamWriter(
					new FileOutputStream(tempFile), toCharset);

			int temp = 0;
			while ((temp = isr.read()) != -1) {
				oos.write(temp);
			}
			isr.close();
			oos.close();

			isr = new InputStreamReader(new FileInputStream(tempFile));
			oos = new OutputStreamWriter(new FileOutputStream(file));
			while ((temp = isr.read()) != -1) {
				oos.write(temp);
			}
			isr.close();
			oos.close();

			tempFile.deleteOnExit(); // 删除临时文件

			return true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return false;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}

	}

	/**
	 * 打印硬盘信息
	 */
	public static void printHDDInfo() {
		File[] files = File.listRoots();
		for (File f : files) {
			System.out.println(f + "\n所有空间：" + f.getTotalSpace() / 1024 / 1024 / 1024 + "G" + "\n已用空间：" + (f.getTotalSpace() - f.getUsableSpace()) / 1024 / 1024 / 1024 + "G" + "\n可用空间：" + f.getUsableSpace() / 1024 / 1024 / 1024 + "G");
			System.out.println("************************");
		}
	}

	/**
	 * 计算代码行数
	 * @param dir指定目录
	 * @param type文件类型，例如：.c、.java、.html
	 */
	static int file_count = 0;
	static int line_count = 0;

	public static void countTheCodeLine(File dir, String type)
			throws IOException {

		if (dir != null && dir.isFile() && dir.getName().endsWith(type)) {
			++file_count;
			LineNumberReader reader = new LineNumberReader(new FileReader(dir));
			while (reader.readLine() != null) {

			}
			line_count += reader.getLineNumber();
			reader.close();
		}
	}
	
	/**
	 * （先删除该目录下的所有文件，最后删除目录）
	 * 递归删除目录和目录下的所有文件
	 * @param dir 要删除的目录
	 */
	public static void deleteDirWithFiles(File dir){
		
		if(!dir.exists())
			throw new IllegalArgumentException("目录不存在！");
		
		File[] files = dir.listFiles();
		for (File file : files) {
			if (!file.isHidden() && file.isDirectory())
				deleteDirWithFiles(file);
			else
				System.out.println("删除文件：" + file.getAbsolutePath() + (file.delete() ? "成功" : "失败！"));
		}
		System.out.println("删除目录：" + dir.getAbsolutePath() + (dir.delete() ? "成功" : "失败！"));
	}
	
	/**
	 * 切割文件
	 * @param file 文件
	 * @param number 需要切割的文件数量(可能会有偏差)
	 * @throws IOException 
	 */
	public static void splitFile(File file,int number) throws IOException{
		
		long totalFileSize = file.length();
		byte[] buf = new byte[(int) (totalFileSize/number)]; // 单个文件的大小
		
		File dir = new File(file.getParent(),"files");
		dir.mkdir();
		
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
		BufferedOutputStream bos = null;
		int len = 0,count = 0;
		while ((len = bis.read(buf))!=-1) {
			bos = new BufferedOutputStream(new FileOutputStream(dir.toString() +File.separator + file.getName() + "part" + (++count)));
			bos.write(buf, 0, len);
			bos.close();
		}
		bis.close();
	}
	
	/**
	 * 合并文件
	 * @param dir 合并该目录下的文件
	 * @param filename 指定新生成的文件名
	 * @throws IOException 
	 */
	public static void mergeFile(File dir,String filename) throws IOException{
		
		List<FileInputStream> fiss = new ArrayList<FileInputStream>();;
		if(dir.isDirectory()){
			File[] files = dir.listFiles();
			for (File file : files) {
				fiss.add(new FileInputStream(file));
			}
		}
		
		final Iterator<FileInputStream> it = fiss.iterator();
		
		Enumeration<FileInputStream> en = new Enumeration<FileInputStream>() {

			@Override
			public boolean hasMoreElements() {
				return it.hasNext();
			}

			@Override
			public FileInputStream nextElement() {
				return it.next();
			}
		};
		
		BufferedInputStream bis = new BufferedInputStream(new SequenceInputStream(en));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filename));
		int len = 0;
		byte[] buf = new byte[1024];
		while ((len = bis.read(buf))!=-1) {
			bos.write(buf, 0, len);
		}
		bos.close();
		bis.close();
	}
}
